package org.btik.video.server.device.udp2;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.btik.context.SimpleContext;
import org.btik.context.thread.SystemControlAble;
import org.btik.light.mobile.meta.VideoChannelType;
import org.btik.video.server.VideoChannel;
import org.btik.video.server.VideoServer;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.concurrent.LinkedBlockingQueue;



public class LHandleUDPDevChannel extends Thread implements  SystemControlAble , VideoServer {

    private final Log log = LogFactory.getLog(LHandleUDPDevChannel.class);

    private volatile boolean runFlag = true;

    private VideoChannel videoChannel;

    /**
     * 帧通道端口号
     */
    private int streamPort;

    private BufferPool bufferPool;


    private FrameSender frameSender;

    private DatagramSocket serverSocket;

    private DatagramPacket helloPush ;


    public void setStreamPort(int streamPort) {
        this.streamPort = streamPort;
    }

    public void setVideoChannel(VideoChannel videoChannel) {
        this.videoChannel = videoChannel;
    }

    public void setBufferPool(BufferPool bufferPool) {
        this.bufferPool = bufferPool;
    }


    @Override
    public synchronized void start() {
        log.info("init buffer pool");

        log.info("start frameSender");
        frameSender = new FrameSender();
        frameSender.start();
        log.info("udp channel loaded");
        helloPush = new DatagramPacket(new byte[0], 0);
        helloPush.setPort(8003);
        super.start();
    }

    @Override
    public void shutdown(String msg) {
        runFlag = false;
        // 无消息导致阻塞时，没有读到flag,帮助退出阻塞
        frameSender.messages.add(new FrameBuffer(new byte[0]));
    }

    @Override
    public void run() {

        try (DatagramSocket serverSocket = new DatagramSocket(streamPort)) {
            this.serverSocket = serverSocket;
            FrameBuffer frameBuffer = bufferPool.getFrameBuffer();
            DatagramPacket datagramPacket = new DatagramPacket(frameBuffer.data, 0, frameBuffer.data.length);
            while (runFlag) {
                serverSocket.receive(datagramPacket);
                frameBuffer.size = datagramPacket.getLength();
                frameSender.messages.add(frameBuffer);
                // 切换缓冲区
                frameBuffer = bufferPool.getFrameBuffer();
                datagramPacket.setData(frameBuffer.data);
            }
        } catch (IOException e) {
            log.info(" start server failed:" + e.getMessage());
        }
    }

    @Override
    public boolean helloPushServer(byte[] sn,String lightServerHost) {
        helloPush.setData(sn);

        try {
            InetAddress address = InetAddress.getByName(lightServerHost);
            helloPush.setAddress(address);
            serverSocket.send(helloPush);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void switchVideoChannel(VideoChannelType videoChannelType) {
        this.videoChannel = SimpleContext.get(videoChannelType.getBeanId());
    }


    class FrameSender extends Thread {
        LinkedBlockingQueue<FrameBuffer> messages = new LinkedBlockingQueue<>();

        @Override
        public void run() {

            try {
                while (runFlag) {
                    FrameBuffer segment = messages.take();
                    try {
                        videoChannel.sendFrame(segment.data, segment.size);
                    } catch (Exception e) {
                        if (runFlag) {
                            log.info("", e);
                        } else {
                            break;
                        }
                    } finally {
                        // 归还到池里
                        bufferPool.returnBuffer(segment);
                    }

                }
            } catch (InterruptedException e) {
                log.info("exit by:" + e);
            }
            log.info("exit : " + getName());
        }

    }
}
